package tau.dimes.web;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;

import tau.dimes.beans.LoginBean;
import tau.dimes.beans.SavedQuery;
import tau.dimes.data.Column;
import tau.dimes.data.Row;
import tau.dimes.data.Table;
import tau.dimes.tasks.LoadTableTask;
import tau.dimes.tasks.QueryToXmlTask;
import tau.dimes.tasks.QueryToXmlTaskResult;
import tau.dimes.tasks.TranslateIpGeoTask;
import tau.dimes.util.ProgressMap;
import tau.dimes.util.XmlHelper;

/**
 * Servlet implementation class QueryDBServlet
 */
public class QueryDBServlet extends HttpServlet
{
	private static final long serialVersionUID = 1L;

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException
	{
		LoginBean login = (LoginBean) request.getSession().getAttribute("login");
		String action = request.getParameter("action");
		if (action != null && !(action.length() == 0))
		{
			try
			{
				if ("previewQuery".equalsIgnoreCase(action))
					previewQuery(request, response);
				else if ("previewIpGeo".equalsIgnoreCase(action))
					previewIpGeo(request, response);
				else if ("saveResults".equalsIgnoreCase(action))
					saveResults(request, response, login);
				else if ("saveQuery".equalsIgnoreCase(action))
					saveQuery(request, response, login);
				else if ("loadQuery".equalsIgnoreCase(action))
					loadQuery(request, response, login);
				else if ("queryProgress".equalsIgnoreCase(action))
					queryProgress(request, response, login);
				else
					sendError(response, "Unknown action");
			}
			catch (Exception e)
			{
				sendError(response, e.getMessage());
			}

			return;
		}
		sendError(response, "Missing action parameter");
	}
		
	private static void previewQuery(HttpServletRequest request, HttpServletResponse response)
		throws Exception
	{
		if (!verifyParameters(request, response, "server", "query", "previewRows"))
			return;

		String server = request.getParameter("server");
		String query = request.getParameter("query");
		int previewRows = Integer.valueOf(request.getParameter("previewRows"));

		LoadTableTask loadTable = new LoadTableTask(server, query, 0, previewRows);
		loadTable.run();
		Table table = loadTable.get();

		JSONObject result = convertToJSON(table);
		response.setContentType("application/json");
		response.getWriter().print(result.toString());
	}

	private static void previewIpGeo(HttpServletRequest request, HttpServletResponse response)
		throws Exception
	{
		if (!verifyParameters(request, response, "server", "query", "previewRows", "ipColumn"))
			return;

		String server = request.getParameter("server");
		String query = request.getParameter("query");
		int previewRows = Integer.valueOf(request.getParameter("previewRows"));
		int ipColumn = Integer.valueOf(request.getParameter("ipColumn"));

		LoadTableTask loadTable = new LoadTableTask(server, query, 0, previewRows);
		loadTable.run();
		Table table = loadTable.get();

		TranslateIpGeoTask translateIpGeo = new TranslateIpGeoTask(table, ipColumn);
		translateIpGeo.run();
		translateIpGeo.get();

		JSONObject result = convertToJSON(table);
		response.setContentType("application/json");
		response.getWriter().print(result.toString());
	}

	private void saveResults(HttpServletRequest request, HttpServletResponse response,
		LoginBean login) throws Exception
	{
		if (!verifyParameters(request, response, "server", "query", "ipColumn", "traceColumn",
			"maxRows", "filename", "requestID"))
			return;

		String server = request.getParameter("server");
		String query = request.getParameter("query");
		int ipColumn = Integer.valueOf(request.getParameter("ipColumn"));
		int traceColumn = Integer.valueOf(request.getParameter("traceColumn"));
		int maxRows = Integer.valueOf(request.getParameter("maxRows"));
		String filename = request.getParameter("filename") + ".trace.xml";
		String requestID = request.getParameter("requestID");

		UserFiles userFiles = new UserFiles(login.getUsername(), this.getServletContext());
		if (userFiles.exists(filename))
		{
			sendError(response, "File already exists");
			return;
		}
		
		if (ProgressMap.contains(requestID))
		{
			sendError(response, "Request ID already exists");
			return;
		}
		ProgressMap.add(requestID);
		
		QueryToXmlTask queryToXmlTask = new QueryToXmlTask(server, query, ipColumn, traceColumn, maxRows, requestID);
		queryToXmlTask.run();
		QueryToXmlTaskResult xmlResult = queryToXmlTask.get();
		
		File outputFile = userFiles.getFile(filename);
		FileWriter fw = new FileWriter(outputFile);
		fw.write(XmlHelper.getStringFromDoc(xmlResult.getDocument()));
		fw.flush();
		fw.close();
		
		JSONObject result = new JSONObject();
		result.put("rows", xmlResult.getRowCount());
		response.setContentType("application/json");
		response.getWriter().print(result.toString());
		
		ProgressMap.remove(requestID);
	}
	
	private void saveQuery(HttpServletRequest request, HttpServletResponse response, 
		LoginBean login) throws Exception
	{
		if (!verifyParameters(request, response, "query", "server",
				"previewRows", "ipColumn", "traceColumn", "filename"))
			return;
		
		SavedQuery savedQuery = new SavedQuery();
		
		savedQuery.setQuery(request.getParameter("query"));
		savedQuery.setServer(request.getParameter("server"));
		savedQuery.setPreviewRows(request.getParameter("previewRows"));
		savedQuery.setIpColumnText(request.getParameter("ipColumn"));
		savedQuery.setTraceColumnText(request.getParameter("traceColumn"));
		savedQuery.setMaxRows(request.getParameter("maxRows"));		
		String filename = request.getParameter("filename") + ".query.xml";

		UserFiles userFiles = new UserFiles(login.getUsername(), this.getServletContext());
		if (userFiles.exists(filename))
		{
			sendError(response, "File already exists");
			return;
		}
		
		File outputFile = userFiles.getFile(filename);
		Serializer serializer = new Persister();
		serializer.write(savedQuery, outputFile);
	}
	
	private void loadQuery(HttpServletRequest request, HttpServletResponse response, 
		LoginBean login) throws Exception
	{
		if (!verifyParameters(request, response, "queryName"))
			return;
		
		String filename = request.getParameter("queryName");
		
		UserFiles userFiles = new UserFiles(login.getUsername(), this.getServletContext());
		if (!userFiles.exists(filename))
		{
			sendError(response, "Query doesn't exist");
			return;
		}
		
		File queryFile = userFiles.getFile(filename);
		Serializer serializer = new Persister();
		SavedQuery savedQuery = serializer.read(SavedQuery.class, queryFile);
		
		JSONObject result = new JSONObject(savedQuery);
		response.setContentType("application/json");
		response.getWriter().print(result.toString());
	}
	
	private void queryProgress(HttpServletRequest request, HttpServletResponse response, 
		LoginBean login) throws Exception
	{
		boolean success = true;
		int progress = 0;
		
		if (verifyParameters(request, response, "requestID"))
		{
			String requestID = request.getParameter("requestID");
			Integer value = ProgressMap.get(requestID);
			if (value != null)
				progress = value;
			else
				success = false;
		}
		else
			success = false;
		
		JSONObject result = new JSONObject();
		result.put("success", success);
		result.put("progress", progress);
		response.setContentType("application/json");
		response.getWriter().print(result.toString());
	}

	private static void sendError(HttpServletResponse response, String errorText)
		throws IOException
	{
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		response.setContentType("text/plain");
		response.getWriter().print(errorText);
	}

	private static boolean verifyParameters(HttpServletRequest request,
		HttpServletResponse response, String... names) throws IOException
	{
		for (String name : names)
		{
			if (request.getParameter(name) == null)
				sendError(response, "Missing " + name + " parameter");
			else if (request.getParameter(name).length() == 0)
				sendError(response, name + " parameter is empty");
			else
				continue;

			return false;
		}

		return true;
	}

	private static JSONObject convertToJSON(Table table) throws JSONException
	{
		JSONObject result = new JSONObject();
		JSONArray columns = new JSONArray();
		for (Column col : table.columns())
			columns.put(col.getColumnName());
		JSONArray rows = new JSONArray();
		for (Row row : table.rows())
			rows.put(row);
		result.put("columns", columns);
		result.put("rows", rows);
		return result;
	}
}
